Skip to content

Conversation

@MartinNowak
Copy link
Member

@MartinNowak MartinNowak commented Oct 4, 2017

  • allows to run con-/destructors before/after CRT startup/shutdown
  • primary use-case is implementing modular startup in druntime itself

required for #6956

@dlang-bot
Copy link
Contributor

dlang-bot commented Oct 4, 2017

Thanks for your pull request, @MartinNowak!

Bugzilla references

Auto-close Bugzilla Description
17868 add pragma(crt_con/destructor)

@MartinNowak MartinNowak force-pushed the fix17868 branch 7 times, most recently from 6fed4d0 to 5522b95 Compare October 5, 2017 21:33
@MartinNowak MartinNowak changed the title fix 17868 - add pragma(crt_con-/destructor[, priority]) fix 17868 - add pragma(crt_con-/destructor) Oct 5, 2017
@MartinNowak MartinNowak force-pushed the fix17868 branch 2 times, most recently from a93389a to 4f9e3ba Compare October 5, 2017 22:15
@andralex
Copy link
Member

andralex commented Oct 6, 2017

I'm okay with this approach as opposed to clever games with static shared this(). @WalterBright ?

@MartinNowak
Copy link
Member Author

This tool is useful despite shared static this() and -betterC, but indeed, once it's there, we don't necessarily need to alter shared static this().


$(RED Note:) The order in which constructors are executed is unspecified.

----
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be three dashes? Or is it three or more?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 or more works

@jacob-carlborg
Copy link
Contributor

Perhaps a test that attaches the pragam to something else than a function.

@MartinNowak MartinNowak force-pushed the fix17868 branch 2 times, most recently from ca1e93e to 03b281a Compare October 6, 2017 11:42
@MartinNowak
Copy link
Member Author

Perhaps a test that attaches the pragam to something else than a function.

Done

@jacob-carlborg
Copy link
Contributor

What about static methods in a class or struct?

@MartinNowak
Copy link
Member Author

MartinNowak commented Oct 6, 2017

What about static methods in a class or struct?

Works, this is a low-level tool (just as pragma(startaddress)), it will just stuff any pointer into the init/fini section, no matter what ABI or params. If that happens to be compatible with extern(C) void func() it won't crash.

@jacob-carlborg
Copy link
Contributor

It would be nice with a test for that.

@MartinNowak
Copy link
Member Author

It would be nice with a test for that.

It's too low-level, I wouldn't want to promise that for all ABIs, use at your own risk.

@andralex
Copy link
Member

andralex commented Oct 6, 2017

This tool is useful despite shared static this() and -betterC, but indeed, once it's there, we don't necessarily need to alter shared static this().

A short list of pros and cons of the approaches woudl be very helpful.

@MartinNowak
Copy link
Member Author

MartinNowak commented Oct 7, 2017

This PR is not about -betterC, let's keep that discussion in #6956 please.

@MartinNowak
Copy link
Member Author

Ping @WalterBright, @andralex. Anything preventing us from adding that feature for low-level runtime hackers and C++ interop?

@andralex
Copy link
Member

@WalterBright I'm good to go with this, will approve.

else if (pd.ident == Id.crt_constructor || pd.ident == Id.crt_destructor)
{
if (pd.args && pd.args.dim != 0)
pd.error("too many arguments");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too many is misleading. Should be arguments are not allowed for crt_constructor and crt_destructor functions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These functions should also be forced to use C linkage.

@WalterBright
Copy link
Member

Thank you @MartinNowak for doing this work, especially the fixing of the *obj.c files.

My complaint centers around using an awkward pragma to do the dirty work. It has the effect of putting every function in its scope into the initializer list, which is a bit bizarre as I can't imagine code needing that. A better placement for the pragma would be inside the function body, which would avoid the need for the recurse() function. The recurse() function provokes questions we shouldn't need to answer (like why not affect the static member functions of an aggregate?).

The complaints around shared static this() are that it changes the semantics. But -betterC is expected to change the semantics to not use druntime, and shared static this() requires druntime. So it is ok to use it for betterC to do the construction/destruction. -betterC changes semantics on other bits of code, too. It's not going to break code.

Another issue is if one is not using -betterC, but wishes to install a function into the C ctor/dtor list. One way is for the user to put such code into a separate file, compile that file with -betterC, and link it in. Another way is to do this:

extern (C) void init() { ... }
pragma(crt_constructor, init);

extern (C) void fini() { ... }
pragma(crt_destructor, fini);

This also avoids the need for the recurse() function.

@MartinNowak
Copy link
Member Author

MartinNowak commented Oct 21, 2017

That's mostly because it's a function attribute (pragma) everywhere else, e.g. gdc/ldc/GCC/clang.
https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html
https://wiki.dlang.org/LDC-specific_language_changes#LDC_global_crt_ctor_and_LDC_global_crt_dtor

The recursion was added to deal with things like the following and maybe template functions.

pragma(crt_constructor)
version (linux) void init() {}

I could instead change it to check that it directly applies to a function.
Behaving like pragma(mangle, seems also reasonable.

@MartinNowak MartinNowak force-pushed the fix17868 branch 3 times, most recently from 13434c2 to 67a59cd Compare October 21, 2017 15:58
@andralex
Copy link
Member

Do we have a precedent of a pragma only applicable to one declaration? If not, would an attribute a la @__crt_constructor be more appropriate?

@WalterBright
Copy link
Member

Is it really a function attribute? It doesn't affect the function signature or ABI. Given the salad of attributes we have, adding more kinda scares me, especially for one so rarely used as this one. Recall how C++'s volatile, rarely used, wound up with an enormously outsized impact on the spec. Whatever we pick should have an impact on the spec in proportion to its use cases.

The recursion was added to deal with things like the following and maybe template functions.

I don't find the recursive example compelling enough to have to explain why it doesn't affect aggregate member functions. Let's pick something that will be easy to document.

@jpf91
Copy link
Contributor

jpf91 commented Oct 22, 2017

Is it really a function attribute? It doesn't affect the function signature or ABI.

Why do you think a function attribute does have to affect the signature or API? We also have UDAs and these are simply defined as compile time only meta-data for certain declarations. Recognize some special cases in the compiler and they are a perfect fit for such situations. LDC and GDC already do this for years:
https://wiki.dlang.org/LDC-specific_language_changes#Attributes
https://wiki.dlang.org/Using_GDC#Attributes
There's also precedent in GCC using function attributes which do not affect ABI or signature instead of introducing pragmas for these cases: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes

The mangle pragma is also a perfect fit for such an UDA. I really don't understand why DMD doesn't just finally follow the other compilers lead here to introduce a dmd.attribute module:

// If you don't want the import in user code, add a public import to object.d instead
import dmd.attribute;

@crt_constructor void foo() {}
@crt_destructor @mangle("some_mangled_name") void bar() {}

You even get compile-time reflection to detect whether any such attribute is applied to a function for free, there's no global namespace pollution, and there are many more benefits. We have this nice UDA feature, why not use it?

@jacob-carlborg
Copy link
Contributor

I really don't understand why DMD doesn't just finally follow the other compilers lead here to introduce a dmd.attribute module:

We already have core.attribute [1]. But of course, not that many knows about it because currently there's only one attribute in that module, @selector, which is used for Objective-C bindings.

[1] https://github.com/dlang/druntime/blob/master/src/core/attribute.d

@MartinNowak
Copy link
Member Author

Can we please keep the attribute discussion off here, it's a necessary, but separate discussion.

So far similar functionality was implemented as pragmas, and there isn't even functionality to recognize special attributes in dmd yet. It's also a pragma declaration in LDC.
Reflection for crt_con-/destructor, weak, or section doesn't seem necessary either.

BTW, that's the second time this review takes a detour into an slightly OT discussion.

Another issue is if one is not using -betterC, but wishes to install a function into the C ctor/dtor list. One way is for the user to put such code into a separate file, compile that file with -betterC, and link it in.

That's hardly feasible as it required changing build tools to compile one module with different flags. It also changes the other semantics of that module.

- allows to run con-/destructors before/after CRT startup/shutdown
- primary use-case is implementing modular startup in druntime itself
@MartinNowak
Copy link
Member Author

MartinNowak commented Oct 23, 2017

I don't find the recursive example compelling enough to have to explain why it doesn't affect aggregate member functions. Let's pick something that will be easy to document.

This PR has already been updated to handle this in the same way as pragma(mangle).
See https://github.com/dlang/dmd/pull/7182/files#diff-a1d1c80dad91d10a8b21b88b0155d54fR1150 and https://github.com/dlang/dmd/pull/7182/files#diff-c9ea52bf15f25ece8c552886cfc67993R6.

@thewilsonator
Copy link
Contributor

My complaint centers around using an awkward pragma to do the dirty work

FWIW in LDC we have
pragma(LDC_global_crt_[c|d]tor [, priority]) { funcdecl(s) } where priority is a ushort.

@MartinNowak
Copy link
Member Author

FWIW in LDC we have
pragma(LDC_global_crt_[c|d]tor [, priority]) { funcdecl(s) } where priority is a ushort.

Yes, as mentioned in #7182 (comment).
After some initial attempts, priority was left out, as it cannot be properly supported on all platforms.

Can we please settle the bike-shedding on pragma vs. attributes vs. pragma with function name. This PR is implemented just like pragma mangle, and now doesn't allow more than one function.

@thewilsonator
Copy link
Contributor

After some initial attempts, priority was left out, as it cannot be properly supported on all platforms.

Does it need to be? it would be nice to drop LDC's specific ones and use the dmd ones (LDCs is also a block pragma but that is less of an issue). Having said that I'm not sure how widely they are used. CRT constructors are rather rare and specifying a priority is even more rare.

Perhaps you could make the priority optional and implementation defined?

@MartinNowak
Copy link
Member Author

No, we went through this, LDC/GDC can keep their pragmas with priority support and map the one added in this PR to the default priority. As you said there is hardly any use-case for priorities to begin with, and even with ldc/gdc priorities were not global, but local (translation unit).

@thewilsonator
Copy link
Contributor

Fair enough.

@andralex andralex merged commit cb2cade into dlang:master Dec 12, 2017
@MartinNowak MartinNowak deleted the fix17868 branch December 13, 2017 07:12
@jacob-carlborg
Copy link
Contributor

@MartinNowak now when this is merged, how does this related to the obj_rtinit [1] function in the compiler? Can that function be completely removed, or parts of it?

[1]

static void obj_rtinit()

@bitraft
Copy link

bitraft commented Dec 14, 2017

crt_constructor is not allow to set static immutable.

static immutable int my_i ;
int test(){
	return 3;
}

shared static this(){
	my_i = test();  // work 
}
pragma(crt_constructor) extern(C) void init1(){
	my_i = test(); //   Error: cannot modify immutable expression my_i
}

@ibuclaw
Copy link
Member

ibuclaw commented Dec 15, 2017

@changloong You could raise a bug on that instead of commenting here.

@ibuclaw
Copy link
Member

ibuclaw commented Dec 15, 2017

@jacob-carlborg - I will assume no, because it needs to be emitted into every DSO.

@bitraft
Copy link

bitraft commented Dec 18, 2017

@ibuclaw thanks for tip
add bug: https://issues.dlang.org/show_bug.cgi?id=18100

@9il
Copy link
Member

9il commented Feb 18, 2018

Thanks for this killer feature for betteC programs.

Tested with mir-cpuid, DMD segfaults if -lib flag is passed
https://issues.dlang.org/show_bug.cgi?id=18456

@Geod24
Copy link
Member

Geod24 commented Feb 19, 2020

Bit late, but specs PR is here: dlang/dlang.org#2757

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.